package com.feonix.common.basedao;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

import com.feonix.common.annotation.ColumnType;
import com.feonix.common.annotation.GeneratorType;
import com.feonix.common.datasource.DataSourceSwitch;
import com.feonix.common.util.JsonUtil;
import com.feonix.common.util.ReflectionUtils;
import com.feonix.common.util.StringUtil;

public class BaseJdbcDAO<Id, T> extends BaseDAO {
	private Class<T> clazz;

	public BaseJdbcDAO() {
		if (getClass().getGenericSuperclass() instanceof ParameterizedType) {
			// 参数化类型
			ParameterizedType parameterizedType = (ParameterizedType) super.getClass().getGenericSuperclass();
			Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
			this.clazz = (Class<T>) actualTypeArguments[1];
			EntityManager.addClass(this.clazz);
		}
		this.pkColumnName = setPkColumnName();
	}

	private List<T> selectObjs(String sql, Map<String, ?> paramMap) {
		EntityRowMap<T> entityRowMap = new EntityRowMap<T>(this.clazz);
		return this.jdbcHelp.query(sql, paramMap, entityRowMap);
	}

	private T selectObj(String sql, Map<String, ?> paramMap) {
		T t = null;
		try {
			EntityRowMap<T> entityRowMap = new EntityRowMap<T>(this.clazz);
			t = this.jdbcHelp.queryForObject(sql, paramMap, entityRowMap);
		} catch (EmptyResultDataAccessException emptyResultDataAccessException) {
		}
		return t;
	}

	public Id insert(T t) {
		Id id = null;
		if (t == null) {
			return id;
		}
		Entity entity = EntityManager.getEntity(this.clazz);
		Map<SqlType, String> entitySqlMap = EntityManager.getEntitySql(this.clazz);
		if ((entity == null) || (entitySqlMap == null)) {
			this.logger.error("this class [" + this.clazz + "] is not mapped");
			return id;
		}
		String str = entitySqlMap.get(SqlType.INSERT);
		id = insertEntity(t, str);
		return id;
	}

	/**
	 * 新增操作，此方法适用于配置了annotation的实体
	 * 
	 * @param t
	 * @param tableName
	 * @return
	 */
	public Id insert(T t, String tableName) {
		Id id = null;
		if (t == null) {
			return id;
		}
		String str = EntityManager.buildInsertSql(this.clazz, tableName);
		id = insertEntity(t, str);
		return id;
	}

	/**
	 * 新增操作，此方法适用于额外建表 并且配置了annotation的实体
	 * 
	 * @param t
	 * @param sql
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private Id insertEntity(T t, String sql) {
		Id id = null;
		Entity entity = EntityManager.getEntity(this.clazz);
		if (StringUtil.isEmpty(sql)) {
			this.logger.error("can't find insert sql in class [" + this.clazz + "]");
			return id;
		}
		List<EntityProperty> entityProperties = entity.getEntityProperties();
		HashMap<String, Object> hashMap = new HashMap<String, Object>();
		for (Iterator<EntityProperty> iterator = entityProperties.iterator(); iterator.hasNext();) {
			EntityProperty entityProperty = iterator.next();
			if (entityProperty.isPk()) { // 如果字段是主键，根据定义的主键的类型来判断插入值
				Query query;
				switch (entityProperty.getGeneratorType().ordinal()) {
				case 1:	//uuid
					String uuid = (String) ReflectionUtils.invokeGetterMethod(t, entityProperty.getFieldName());
					if (StringUtil.isEmpty(uuid)) {
						uuid = StringUtil.getUUIDString();
					}
					hashMap.put(entityProperty.getColumnName(), uuid);
					if (entityProperty == entity.getIdProperty()) {
						id = (Id) uuid;
					}
					break;
				case 2:	//自定义
					BaseIdGenerator baseIdGenerator = (BaseIdGenerator) entityProperty.getGenerator();
					id = (Id) ReflectionUtils.invokeGetterMethod(t, entityProperty.getFieldName());
					if (id == null) {
						id = (Id) baseIdGenerator.generate();
					}
					hashMap.put(entityProperty.getColumnName(), id);
					break;
				case 3:	//maxid
					id = (Id) ReflectionUtils.invokeGetterMethod(t, entityProperty.getFieldName());
					if (id == null) {
						String maxId = EntityManager.getEntitySql(this.clazz).get(SqlType.MAXID);
						query = createQuery(maxId);
						Integer iid = queryForInteger(query);
						if (iid == null) {
							iid = Integer.valueOf(0);
						}
						iid = Integer.valueOf(iid.intValue() + 1);
						id = (Id) iid;
					}
					hashMap.put(entityProperty.getColumnName(), id);
					break;
				default: // 自增长跳过赋值
					continue;
				}
			}else{ //普通字段塞值
				Object commonField = ReflectionUtils.invokeGetterMethod(t, entityProperty.getFieldName());
		        if (entityProperty.getColumnType() == ColumnType.JSON) {
		        	commonField = JsonUtil.objectToString(commonField);
		        }
		        hashMap.put(entityProperty.getColumnName(), commonField);
			}
		}
		String columnName = null;
		KeyHolder generatedKeyHolder = null;
		EntityProperty idEntityProperty = entity.getIdProperty();
		if (idEntityProperty != null) {
			columnName = idEntityProperty.getColumnName();
			if (idEntityProperty.getGeneratorType() == GeneratorType.NATIVE) {
				generatedKeyHolder = new GeneratedKeyHolder();
				this.jdbcHelp.update(sql, new MapSqlParameterSource(hashMap), generatedKeyHolder,
						new String[] { columnName });
				id = (Id) new Integer(generatedKeyHolder.getKey().intValue());
			} else {
				this.jdbcHelp.update(sql, hashMap);
			}
			ReflectionUtils.invokeSetterMethod(t, idEntityProperty.getFieldName(), id, idEntityProperty.getFieldType());
		} else {
			id = (Id) new Integer(this.jdbcHelp.update(sql, hashMap));
		}
		return id;

	}

	/**
	 * 修改操作，此方法适用于配置了annotation的实体
	 * 
	 * @param t
	 * @return
	 */
	public boolean update(T t) {
		boolean bool = false;
		if (t == null) {
			return bool;
		}
		Entity entity = EntityManager.getEntity(this.clazz);
		Map<SqlType, String> map = EntityManager.getEntitySql(this.clazz);
		if ((entity == null) || (map == null)) {
			this.logger.error("this class [" + this.clazz + "] is not mapped");
			return bool;
		}
		String str = (String) map.get(SqlType.UPDATE);
		bool = updateEntity(t, str);
		return bool;
	}

	/**
	 * 修改操作，此方法适用于额外建表 并且配置了annotation的实体
	 * 
	 * @param t
	 * @param tableName
	 * @return
	 */
	public boolean update(T t, String tableName) {
		boolean bool = false;
		if (t == null) {
			return bool;
		}
		Entity entity = EntityManager.getEntity(this.clazz);
		if (entity == null) {
			this.logger.error("this class [" + this.clazz + "] is not mapped");
			return bool;
		}
		String str = EntityManager.buildUpdateSql(this.clazz, tableName);
		bool = updateEntity(t, str);
		return bool;
	}

	/**
	 * 
	 * @param t
	 * @param sql
	 * @return
	 */
	private boolean updateEntity(T t, String sql) {
		boolean isSuccess = false;
		Entity entity = EntityManager.getEntity(this.clazz);
		Map<SqlType, String> sqlMap = EntityManager.getEntitySql(this.clazz);
		if ((entity == null) || (sqlMap == null)) {
			this.logger.error("this class [" + this.clazz + "] is not mapped");
			return isSuccess;
		}
		EntityProperty entityProperty = entity.getIdProperty();
		if (entityProperty == null) {
			this.logger.error("can't find id in class [" + this.clazz + "]");
			return isSuccess;
		}
		Object getMethod = ReflectionUtils.invokeGetterMethod(t, entityProperty.getFieldName());
		if (getMethod == null) {
			this.logger.error("id value in class [" + this.clazz + "] is null");
			return isSuccess;
		}
		if (StringUtil.isEmpty(sql)) {
			this.logger.error("can't find update sql in class [" + this.clazz + "]");
			return isSuccess;
		}
		List<EntityProperty> entityProperties = entity.getEntityProperties();
		HashMap<String, Object> updateMap = new HashMap<String, Object>();
		for (EntityProperty entityProperty1 : entityProperties) {
			if (!(entityProperty1.isUpdate())) {
				continue;
			}
			Object value = ReflectionUtils.invokeGetterMethod(t, entityProperty1.getFieldName());
			if (entityProperty1.getColumnType() == ColumnType.JSON) {
				value = JsonUtil.objectToString(value);
			}
			updateMap.put(entityProperty1.getColumnName(), value);
		}
		try {
			this.jdbcHelp.update(sql, updateMap);
			isSuccess = true;
		} catch (DataAccessException dataAccessException) {
			this.logger.error("update error", dataAccessException);
		}
		return isSuccess;
	}

	/**
	 * 按照主键id删除，此方法适用于配置了annotation的实体
	 * 
	 * @param id
	 * @return
	 */
	public boolean deleteById(Id id) {
		boolean bool = false;
		if (id == null) {
			return bool;
		}

		Entity entity = EntityManager.getEntity(this.clazz);
		Map<SqlType, String> sqlMap = EntityManager.getEntitySql(this.clazz);
		if ((entity == null) || (sqlMap == null)) {
			this.logger.error("this class [" + this.clazz + "] is not mapped");
			return bool;
		}
		String str = (String) sqlMap.get(SqlType.DELETE);
		bool = deleteByEntityId(id, str);
		return bool;
	}

	/**
	 * 按照主键id删除，此方法适用于额外建表 并且配置了annotation的实体
	 * 
	 * @param id
	 * @param tableName
	 * @return
	 */
	public boolean deleteById(Id id, String tableName) {
		boolean bool = false;
		if (id == null) {
			return bool;
		}
		Entity entity = EntityManager.getEntity(this.clazz);
		if (entity == null) {
			this.logger.error("this class [" + this.clazz + "] is not mapped");
			return bool;
		}
		String str = EntityManager.buildDeleteSql(this.clazz, tableName);
		bool = deleteByEntityId(id, str);
		return bool;
	}

	private boolean deleteByEntityId(Id id, String sql) {
		boolean isSuccess = false;
		Entity entity = EntityManager.getEntity(this.clazz);
		EntityProperty entityProperty = entity.getIdProperty();
		if (entityProperty == null) {
			this.logger.error("can't find id in class [" + this.clazz + "]");
			return isSuccess;
		}
		if (StringUtil.isEmpty(sql)) {
			this.logger.error("can't find delete sql in class [" + this.clazz + "]");
			return isSuccess;
		}
		HashMap<String, Object> hashMap = new HashMap<String, Object>();
		hashMap.put(entityProperty.getColumnName(), id);
		try {
			this.jdbcHelp.update(sql, hashMap);
			isSuccess = true;
		} catch (DataAccessException dataAccessException) {
			this.logger.error("deleteById error id:" + id, dataAccessException);
		}
		return isSuccess;
	}

	/**
	 * 按照主键id删除，此方法适用于配置了annotation的实体
	 * 
	 * @param ids
	 *            一批主键
	 * @return
	 */
	public boolean deleteByIds(List<Id> ids) {
		boolean bool = false;
		if (CollectionUtils.isEmpty(ids)) {
			return bool;
		}

		Entity entity = EntityManager.getEntity(this.clazz);
		Map<SqlType, String> entitySql = EntityManager.getEntitySql(this.clazz);
		if ((entity == null) || (entitySql == null)) {
			this.logger.error("this class [" + this.clazz + "] is not mapped");
			return bool;
		}
		String str = (String) entitySql.get(SqlType.DELETE_FROM_IDS);
		bool = deleteByEntityIds(ids, str);
		return bool;
	}

	/**
	 * 按照主键id删除，此方法适用于额外建表 并且配置了annotation的实体
	 * 
	 * @param ids
	 *            一批主键
	 * @param tableName
	 *            表名称
	 * @return
	 */
	public boolean deleteByIds(List<Id> ids, String tableName) {
		boolean bool = false;
		if (CollectionUtils.isEmpty(ids)) {
			return bool;
		}

		Entity entity = EntityManager.getEntity(this.clazz);
		if (entity == null) {
			this.logger.error("this class [" + this.clazz + "] is not mapped");
			return bool;
		}
		String str = EntityManager.buildDeleteFromIdsSql(this.clazz, tableName);
		bool = deleteByEntityIds(ids, str);
		return bool;
	}

	private boolean deleteByEntityIds(List<Id> ids, String sql) {
		boolean isSuccess = false;
		Entity entity = EntityManager.getEntity(this.clazz);
		EntityProperty entityProperty = entity.getIdProperty();
		if (entityProperty == null) {
			this.logger.error("can't find id in class [" + this.clazz + "]");
			return isSuccess;
		}
		if (StringUtil.isEmpty(sql)) {
			this.logger.error("can't find delete_from_ids sql in class [" + this.clazz + "]");
			return isSuccess;
		}
		HashMap<String, List<Id>> idsMap = new HashMap<String, List<Id>>();
		idsMap.put(entityProperty.getColumnName(), ids);
		try {
			this.jdbcHelp.update(sql, idsMap);
			isSuccess = true;
		} catch (DataAccessException dataAccessException) {
			this.logger.error("deleteByIds error ids:" + ids, dataAccessException);
		}
		return isSuccess;
	}

	/**
	 * 按照主键id查找一个实体，此方法适用于配置了annotation的实体
	 * 
	 * @param id
	 * @return
	 */
	public T queryForEntityById(Id id) {
		T t = null;
		if (id == null) {
			return t;
		}
		Entity entity = EntityManager.getEntity(this.clazz);
		Map<SqlType, String> entitySql = EntityManager.getEntitySql(this.clazz);
		if ((entity == null) || (entitySql == null)) {
			this.logger.error("this class [" + this.clazz + "] is not mapped");
			return t;
		}
		String str = (String) entitySql.get(SqlType.SELECT_FROM_ID);
		t = queryForEntity(id, str);
		return t;
	}

	/**
	 * 按照主键id查找一个实体，此方法适用于额外建表 并且配置了annotation的实体
	 * 
	 * @param id
	 * @param tableName
	 * @return
	 */
	public T queryForEntityById(Id id, String tableName) {
		T t = null;
		if (id == null) {
			return t;
		}
		Entity entity = EntityManager.getEntity(this.clazz);
		if (entity == null) {
			this.logger.error("this class [" + this.clazz + "] is not mapped");
			return t;
		}
		String sql = EntityManager.buildSelectFromIdSql(this.clazz, tableName);
		t = queryForEntity(id, sql);
		return t;
	}

	private T queryForEntity(Id id, String sql) {
		T t = null;
		if (id == null) {
			return t;
		}
		Entity entity = EntityManager.getEntity(this.clazz);
		EntityProperty entityProperty = entity.getIdProperty();
		if (entityProperty == null) {
			this.logger.error("can't find id in class [" + this.clazz + "]");
			return t;
		}
		if (StringUtil.isEmpty(sql)) {
			this.logger.error("can't find select_from_id sql in class [" + this.clazz + "]");
			return t;
		}
		HashMap<String, Id> idsMap = new HashMap<String, Id>();
		idsMap.put(entityProperty.getColumnName(), id);
		return selectObj(sql, idsMap);
	}

	/**
	 * 查找一个实体，此方法适用于配置了annotation的实体
	 * 
	 * @param query
	 * @return
	 */
	public T queryForEntity(Query query) {
		T t = null;
		String str = query.getSql();
		if (StringUtil.isEmpty(str)) {
			return t;
		}
		Entity entity = EntityManager.getEntity(this.clazz);
		Map<SqlType, String> entitySql = EntityManager.getEntitySql(this.clazz);
		if ((entity == null) || (entitySql == null)) {
			this.logger.error("this class [" + this.clazz + "] is not mapped");
			return t;
		}

		return selectObj(str, query.getParams());
	}

	/**
	 * 查找一批实体，此方法适用于配置了annotation的实体
	 * 
	 * @param query
	 * @return
	 */
	public List<T> queryForEntities(Query query) {
		List<T> list = null;
		String str = query.getSql();
		if (StringUtil.isEmpty(str)) {
			this.logger.error("sql is null");
			return list;
		}
		Entity entity = EntityManager.getEntity(this.clazz);
		Map<SqlType, String> entitySql = EntityManager.getEntitySql(this.clazz);
		if ((entity == null) || (entitySql == null)) {
			this.logger.error("this class [" + this.clazz + "] is not mapped");
			return list;
		}

		return selectObjs(str, query.getParams());
	}

	/**
	 * 获得配置了annotation的实体的 select语句
	 * 
	 * @return
	 */
	public String getEntitySql() {
		String str = null;
		Entity entity = EntityManager.getEntity(this.clazz);
		Map<SqlType, String> entitySql = EntityManager.getEntitySql(this.clazz);
		if ((entity == null) || (entitySql == null)) {
			this.logger.error("this class [" + this.clazz + "] is not mapped");
			return str;
		}

		str = entitySql.get(SqlType.SELECT);
		if (StringUtil.isEmpty(str)) {
			this.logger.error("can't find select sql in class [" + this.clazz + "]");
			return str;
		}
		return str + " ";
	}

	public boolean createTable(Class<?> clazz, String tableName) {
		String str1 = EntityManager.buildTableSql(clazz, tableName);
		executeForDDL(str1);
		if (DataSourceSwitch.getDbType() == 1) {
			String buildSeqDLL = EntityManager.buildSeq(clazz, tableName);
			String triggerDLL = EntityManager.buildTrigger(clazz, tableName);
			String enableSeqDll = EntityManager.enableSeq(clazz, tableName);
			executeForDDL(buildSeqDLL);
			executeForDDL(triggerDLL);
			executeForDDL(enableSeqDll);
		}
		return true;
	}
}
